module HTTP
    CR  = "\015"
    LF  = "\012"
    EOL = CR + LF

    class Status
        attr_reader :code, :message

        def initialize(code, message)
            @code, @message=code, message
        end

        def to_s
            "#{@code} #{@message}"
        end
    end

    OK                  = Status.new(200, "OK")
    PARTIAL_CONTENT     = Status.new(206, "Partial Content")
    MULTIPLE_CHOICES    = Status.new(300, "Multiple Choices")
    MOVED               = Status.new(301, "Moved Permanently")
    REDIRECT            = Status.new(302, "Found")
    NOT_MODIFIED        = Status.new(304, "Not Modified")
    BAD_REQUEST         = Status.new(400, "Bad Request")
    AUTH_REQUIRED       = Status.new(401, "Authorization Required")
    FORBIDDEN           = Status.new(403, "Forbidden")
    NOT_FOUND           = Status.new(404, "Not Found")
    METHOD_NOT_ALLOWED  = Status.new(405, "Method Not Allowed")
    NOT_ACCEPTABLE      = Status.new(406, "Not Acceptable")
    LENGTH_REQUIRED     = Status.new(411, "Length Required")
    PRECONDITION_FAILED = Status.new(412, "Rrecondition Failed")
    SERVER_ERROR        = Status.new(500, "Internal Server Error")
    NOT_IMPLEMENTED     = Status.new(501, "Method Not Implemented")
    BAD_GATEWAY         = Status.new(502, "Bad Gateway")
    VARIANT_ALSO_VARIES = Status.new(506, "Variant Also Negotiates")
end

class Mime

    DEFAULT_CONTENT_TYPE = "text/html; charset=UTF-8"

    @@types = Hash.new

    def Mime.set(ext, type)
	ext = ext[/[^\.]+/]
	@@types[ext] = type
    end

    def Mime.add(type, *exts)
	exts.each{ |ext|
	    Mime.set(ext, type)
	}
    end

    def Mime.load(types)
       types.each_line { |line|
            next if line.strip!.empty?
	    mime, *exts = line.split
	    next if !mime || mime.empty?
	    add(mime, *exts)
       }
    end

    def Mime.load_file(filename)
       Mine.load(IO.read(filename)) if File.exists?(filename)
    end

    def Mime.types
	@@types
    end

    def Mime.type(ext)
	ext = ext[/[^\.]+/]
	@@types[ext] || "text/plain"
    end

    def Mime.file_type(filename)
        filename = filename.path if filename.class == File
        ext = File.extname(filename)
        ext = ext[/[^\.]+/]
        return nil if !ext || ext.empty?
	@@types[ext] || "text/plain"
    end

end

class Attachment < File

    attr_reader :name, :filename, :content_type

    def initialize(name, filename, content_type, store=@@default_store)
	super(filename, "w")
	@name, @filename, @content_type = name, filename, content_type
	binmode if defined? binmode
    end

end

module Multipart

    include HTTP

    MULTIPART = %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|m
    DISPOSITION = /Content-Disposition: (.*?)$/mi
    CONTENT_TYPE = /Content-Type: (.*)/mi
    EOLEOL = /#{EOL}#{EOL}/m
    NAME_VALUE = /(\S+)="?([^\";]*)"?/

    BUFFER_SIZE = 10 * 1024

    attr_reader :multipart

    def parse(stream, content_type, content_length)

	m = MULTIPART.match(content_type)
        unless m.matched?
            @multipart = false
	    post = stream.read(content_length)
            post = CGI::parse(post) if post
            return post
        end

        @multipart = true
	boundary = "--" + m[1].dup

	stop_r = /\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{boundary}([\r\n]{1,2}|--)/m
	header_r = /\A((?:.|\n)*?#{EOL})#{EOL}/m
	block_r = /#{boundary}(?:#{EOL}|--)/m

	stream.binmode if defined? stream.binmode

	boundary_size = boundary.size + EOL.size
	eol_boundary_size = boundary_size + EOL.size
	len = content_length - boundary_size

	status = stream.read(boundary_size)
	raise EOFError, "no content body" unless status
	raise EOFError, "bad content body" if boundary + EOL != status

	buf = ""
	bufsize = BUFFER_SIZE
    
        attachments = Array.new 

	loop {

	    head = nil
	    body = nil

	    until head && block_r.match(buf)
		next if !head && EOLEOL.match(buf) && 
		    buf.sub!(header_r) { |match|
		    head = match[1].dup

		    disp = Hash.new
		    s = StringScanner.new(DISPOSITION.match(head)[1])
		    until(s.eos?)
			s.scan_until(NAME_VALUE)
			disp[s[1].downcase] = s[2] if s.matched?
		    end

		    file = disp["filename"]
		    file = CGI::unescape(file) unless 
			/MSIE/mi.match(user_agent())
		    type = CONTENT_TYPE.match(head)
		    type = type[1] if type
		    body = Attachment.new(disp["name"], file, type)
		    ""
		}

		size = buf.size - eol_boundary_size
		body.write(buf.slice!(0...size)) if buf.size > eol_boundary_size
		bufsize = len if len < bufsize
		bytes = stream.read(bufsize)

		raise EOFError, "bad content body" if bytes.nil?

		buf << bytes
		len -= bytes.size
	   end

	    buf.sub!(stop_r) { |match|
		body.write match[1]
		len = -1 if "--" == match[2]
		""
	    }

	    body.close

	    attachments << body
	    break if buf.size == 0 || len === -1
	}

	attachments
    end
end

Mime.load <<-TYPES
application/andrew-inset        ez
application/atom+xml            atom
application/mac-binhex40        hqx
application/mac-compactpro      cpt
application/mathml+xml          mathml
application/msword              doc
application/octet-stream        bin dms lha lzh exe class so dll dmg
application/oda                 oda
application/ogg                 ogg
application/pdf                 pdf
application/pgp-encrypted       pgp
application/postscript          ai eps ps
application/rdf+xml             rdf
application/smil                smi smil
application/srgs                gram
application/srgs+xml            grxml
application/vnd.mif             mif
application/vnd.mozilla.xul+xml xul
application/vnd.ms-excel        xls
application/vnd.ms-powerpoint   ppt
application/vnd.wap.wbxml       wbxml
application/vnd.wap.wmlc        wmlc
application/vnd.wap.wmlscriptc  wmlsc
application/voicexml+xml        vxml
application/x-bcpio             bcpio
application/x-cdlink            vcd
application/x-chess-pgn         pgn
application/x-cpio              cpio
application/x-csh               csh
application/x-director          dcr dir dxr
application/x-dvi               dvi
application/x-futuresplash      spl
application/x-gtar              gtar
application/x-gzip              gz
application/x-hdf               hdf
application/x-javascript        js
application/x-koan              skp skd skt skm
application/x-latex             latex
application/x-netcdf            nc cdf
application/x-sh                sh
application/x-shar              shar
application/x-shockwave-flash   swf
application/x-stuffit           sit
application/x-sv4cpio           sv4cpio
application/x-sv4crc            sv4crc
application/x-tar               tar
application/x-tcl               tcl
application/x-tex               tex
application/x-texinfo           texinfo texi
application/x-troff             t tr roff
application/x-troff-man         man
application/x-troff-me          me
application/x-troff-ms          ms
application/x-ustar             ustar
application/x-wais-source       src
application/xhtml+xml           xhtml xht
application/xslt+xml            xslt
application/xml                 xml xsl
application/xml-dtd             dtd
application/xml-external-parsed-entity
application/zip                 zip
audio/basic                     au snd
audio/midi                      mid midi kar
audio/mpa                       mpa
audio/mpeg                      mpga mp2 mp3
audio/x-aiff                    aif aiff aifc
audio/x-mpegurl                 m3u
audio/x-pn-realaudio            ram ra
application/vnd.rn-realmedia    rm
audio/x-wav                     wav
chemical/x-pdb                  pdb
chemical/x-xyz                  xyz
image/bmp                       bmp
image/cgm                       cgm
image/gif                       gif
image/ief                       ief
image/jpeg                      jpeg jpg jpe
image/png                       png
image/svg+xml                   svg
image/tiff                      tiff tif
image/vnd.djvu                  djvu djv
image/vnd.wap.wbmp              wbmp
image/x-cmu-raster              ras
image/x-icon                    ico
image/x-portable-anymap         pnm
image/x-portable-bitmap         pbm
image/x-portable-graymap        pgm
image/x-portable-pixmap         ppm
image/x-rgb                     rgb
image/x-xbitmap                 xbm
image/x-xpixmap                 xpm
image/x-xwindowdump             xwd
model/iges                      igs iges
model/mesh                      msh mesh silo
model/vrml                      wrl vrml
text/calendar                   ics ifb
text/css                        css
text/html                       html htm
text/plain                      asc txt
text/richtext                   rtx
text/rtf                        rtf
text/sgml                       sgml sgm
text/tab-separated-values       tsv
text/vnd.wap.wml                wml
text/vnd.wap.wmlscript          wmls
text/x-setext                   etx
video/mpeg                      mpeg mpg mpe
video/quicktime                 qt mov
video/vnd.mpegurl               mxu m4u
video/x-msvideo                 avi
video/x-sgi-movie               movie
x-conference/x-cooltalk         ice

TYPES
